<template>
  <div>
    <div class="headerStyle">
      <div>
        <span class="backStyle" @click="backFun"></span>
        <span @click="processUndo()" :class="revocable?'undo':'undo nouse'"></span>
        <span @click="processRedo()" :class="recoverable?'redo':'redo nouse'"></span>
      </div>
      <div class="titleStyle">{{ name }}</div>
      <div>
        <!-- <el-button type="primary" size="mini">分享协作</el-button> -->
        <span class="downLoad" @click="svgToPng"></span>
        <span class="publish" @click="saveFun"></span>
      </div>
    </div>
    <div class="containers">
      <div class="myStyle">图形库</div>
      <div class="canvas" ref="canvas"></div>
      <div id="js-properties-panel" class="panel"></div>
      <div v-if="shape != null&&isShow" class="right-rail">
        <span class="arrowWrap" @click="()=>{
          isShow = false;
        }"><span class="arrow"></span></span>
        <h1 style="padding: 20px 0 0 10px;font-weight: 700;">节点属性</h1>
        <el-form ref="form" :model="form" label-width="80px" style="padding: 10px;" label-position="top">
          <el-form-item label="标题" v-if="shape && shape.businessObject.$attrs.custom != 3">
            <el-input v-model="form.title" type="textarea" :maxlength="6"></el-input>
          </el-form-item>
          <!--圆圈使用-->
          <el-form-item label="时限要求" v-if="shape && shape.businessObject.$attrs.custom == 1">
            <el-date-picker v-model="form.time" type="year" placeholder="选择" value-format="yyyy" format="yyyy">
            </el-date-picker>
          </el-form-item>
          <el-form-item label="输入条件" v-if="shape && shape.businessObject.$attrs.custom == 1">
            <el-input type="textarea" v-model="form.conditions" rows="4"></el-input>
          </el-form-item>
          <el-form-item label="启动标准" v-if="shape && shape.businessObject.$attrs.custom == 1">
            <el-input type="textarea" v-model="form.standard" rows="4"></el-input>
          </el-form-item>
          <!--圆圈使用end-->
          <!--正方形使用-->
          <el-form-item label="阶段性成果形式" v-if="shape && shape.businessObject.$attrs.custom == 2">
            <el-input type="textarea" v-model="form.conditions" rows="2" :maxlength="14"></el-input>
          </el-form-item>
          <el-form-item label="工作标准" v-if="shape && shape.businessObject.$attrs.custom == 2">
            <el-input type="textarea" v-model="form.standard" rows="2" :maxlength="14"></el-input>
          </el-form-item>
          <el-form-item label="完成时限" v-if="shape && shape.businessObject.$attrs.custom == 2">
            <el-date-picker v-model="form.time" type="year" placeholder="选择" value-format="yyyy" format="yyyy">
            </el-date-picker>
          </el-form-item>
          <!--正方形使用end-->
          <el-form-item label="标题" v-if="shape && shape.businessObject.$attrs.custom == 3">
            <el-input v-model="form.title" type="textarea" :maxlength="10"></el-input>
          </el-form-item>
          <el-form-item label="责任单位">
            <el-select v-model="form.unit" placeholder="请选择责任单位">
              <el-option label="中心" value="中心"></el-option>
              <el-option label="科技委" value="科技委"></el-option>
            </el-select>
          </el-form-item>
        </el-form>
      </div>
      <!-- 用于打开本地文件-->
      <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" />
    </div>
  </div>
</template>

<script>
// 引入相关的依赖
// import { xmlStr } from '@/mock/xmlStr'
import { detail, update } from "@/api/file"
import html2canvas from "html2canvas";
import CustomModeler from '@/components/bpmnProcessModify/customModeler'
import { mapState, mapMutations } from 'vuex'
import DefaultEmptyXML from '@/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty'
export default {
  name: '',
  components: {},
  // 生命周期 - 创建完成（可以访问当前this实例）
  created() { },
  // 生命周期 - 载入后, Vue 实例挂载到实际的 DOM 操作完成，一般在该过程进行 Ajax 交互
  mounted() {
    this.init()
    this.$once('hook:beforeDestroy', () => {
      if (this.bpmnModeler) this.bpmnModeler.destroy()
      this.$emit('destroy', this.bpmnModeler)
      this.bpmnModeler = null
    })
  },
  data() {
    return {
      // bpmn建模器
      bpmnModeler: null,
      container: null,
      canvas: null,
      shape: null,
      isShow: true,
      input: '',
      form: {
        title: '',
        conditions: '',
        standard: '',
        time: '',
        unit: '',
      },
      name: '',
      directoryId: '',
      headerButtonSize: '',
      status: '',
      recoverable: false,
      revocable: false
    }
  },
  watch: {
    // 如果 `form` 发生改变，这个函数就会运行
    form: {
      handler(newInput) {
        const modeling = this.bpmnModeler.get('modeling')
        console.log("form发生改变");
        modeling.updateProperties(this.shape, {
          title: newInput.title,
          standard: newInput.standard,
          conditions: newInput.conditions,
          time: newInput.time,
          unit: newInput.unit
        })
      },
      deep: true // 开启深度监听，默认是false
    },
  },
  // 方法集合
  methods: {
    ...mapMutations(['TOGGLENODEVISIBLE']),
    init() {
      console.log('%c [  ]-149', 'font-size:13px; background:pink; color:#bf2c9f;', this.$route.query.id)
      // 获取到属性ref为“canvas”的dom节点
      const canvas = this.$refs.canvas
      // 建模
      this.bpmnModeler = new CustomModeler({
        container: canvas,
        //添加控制板
        propertiesPanel: {
          parent: '#js-properties-panel'
        },
        additionalModules: []
      })
      this.createNewDiagram()
    },
    backFun() {
      this.$router.go(-1)
    },
    exportPicFun() {
      html2canvas(this.$refs.canvas, {
        backgroundColor: "#ffffff",
      }).then((canvas) => {
        var imgData = canvas.toDataURL("image/jpeg");
        let aLink = document.createElement("a");
        aLink.style.display = "none";
        aLink.href = imgData;
        aLink.download = "img.png";
        document.body.appendChild(aLink);
        aLink.click();
        document.body.removeChild(aLink);
      });
    },
    // createNewDiagram() {
    //   // 将字符串转换成图显示出来
    //   this.bpmnModeler.importXML(xmlStr, err => {
    //     if (err) {
    //       // console.error(err)
    //     } else {
    //       // 这里是成功之后的回调, 可以在这里做一系列事情
    //       this.success()
    //     }
    //   })
    // },
    success() {
      // console.log('创建成功!')
      this.addBpmnListener()
      this.addEventBusListener()
    },
    addEventBusListener() {
      // 监听 element
      let that = this
      const eventBus = this.bpmnModeler.get('eventBus')
      const elementRegistry = this.bpmnModeler.get('elementRegistry')
      const eventTypes = ['element.click', 'element.changed']
      eventTypes.forEach(function (eventType) {
        eventBus.on(eventType, function (e) {
          // console.log(e, 'event')
          if (!e || e.element.type == 'bpmn:Process') return
          if (eventType === 'element.changed') {
            // console.log(e.gfx,'gfx');
            if (e.gfx && e.element.type != "bpmn:SequenceFlow") {
              that.shape = e.element ? elementRegistry.get(e.element.id) : e.shape
              if (that.shape.type === 'bpmn:Task') {
                that.input = that.shape.businessObject.$attrs.text
                that.form.title = that.shape.businessObject.$attrs.title
                that.form.conditions = that.shape.businessObject.$attrs.conditions
                that.form.standard = that.shape.businessObject.$attrs.standard
                that.form.time = that.shape.businessObject.$attrs.time
                that.form.unit = that.shape.businessObject.$attrs.unit
              }
            }
            else {
              that.shape = null
            }
          } else if (eventType === 'element.click' && e.element.type != "bpmn:SequenceFlow") {
            that.isShow = true;
            // var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
            that.shape = e.element ? elementRegistry.get(e.element.id) : e.shape
            if (that.shape.type === 'bpmn:Task') {
              that.input = that.shape.businessObject.$attrs.text
              that.form.title = that.shape.businessObject.$attrs.title
              that.form.conditions = that.shape.businessObject.$attrs.conditions
              that.form.standard = that.shape.businessObject.$attrs.standard
              that.form.time = that.shape.businessObject.$attrs.time
              that.form.unit = that.shape.businessObject.$attrs.unit
            }
          }
        })
      })
      eventBus.on('commandStack.changed', async event => {
        try {
          that.recoverable = that.bpmnModeler.get('commandStack').canRedo()
          that.revocable = that.bpmnModeler.get('commandStack').canUndo()
          let { xml } = await that.bpmnModeler.saveXML({ format: true })
          that.$emit('commandStack-changed', event)
          that.$emit('input', xml)
          that.$emit('change', xml)
        } catch (e) {
          console.error(`[Process Designer Warn]: ${e.message || e}`)
        }
      })
    },
    close() {
      // window.localStorage.setItem('nodeVisible', 'false')
      this.TOGGLENODEVISIBLE(false)
    },
    addBpmnListener() {
      const that = this
      // 获取a标签dom节点
      const downloadLink = this.$refs.saveDiagram
      // 给图绑定事件，当图有发生改变就会触发这个事件
      this.bpmnModeler.on('commandStack.changed', function () {
        // this.recoverable = this.bpmnModeler.get('commandStack').canRedo()
        // this.revocable = this.bpmnModeler.get('commandStack').canUndo()
        // this.$emit('commandStack-changed', event)
        that.saveDiagram(function (err, xml) {
          that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
        })
      })
    },
    // 下载为bpmn格式,done是个函数，调用的时候传入的
    saveDiagram(done) {
      // 把传入的done再传给bpmn原型的saveXML函数调用
      this.bpmnModeler.saveXML({ format: true }, function (err, xml) {
        done(err, xml)
      })
    },
    // // 当图发生改变的时候会调用这个函数，这个data就是图的xml 旧的
    // setEncoded(link, name, data) {
    //   // 把xml转换为URI，下载要用到的
    //   const encodedData = encodeURIComponent(data)
    //   // 下载图的具体操作,改变a的属性，className令a标签可点击，href令能下载，download是下载的文件的名字
    //   // console.log(link, name, data)
    //   let xmlFile = new File([data], 'test.bpmn')
    //   //   console.log(xmlFile)
    //   if (data) {
    //     link.className = 'active'
    //     link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
    //     link.download = name
    //   }
    // },
    // 根据所需类型进行转码并返回下载地址
    setEncoded(type, filename = 'diagram', data) {
      const encodedData = encodeURIComponent(data)
      return {
        filename: `${filename}.${type}`,
        href: `data:application/${type === 'svg' ? 'text/xml' : 'bpmn20-xml'
          };charset=UTF-8,${encodedData}`,
        data: data
      }
    },
    async saveFun() {
      this.$confirm(`确定保存吗？`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(async () => {
          const { xml } = await this.bpmnModeler.saveXML();
          const { svg } = await this.bpmnModeler.saveSVG()
          update({
            id: Number(this.$route.query.id),
            name: this.name,
            directoryId: this.directoryId,
            content: xml,
            status: this.status,
            picPath: svg
          }).then(() => {
            this.backFun()
          })
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消",
          });
        });

    },
    // 下载流程图到本地
    async downloadProcess(type, name) {
      try {
        const _this = this
        // 按需要类型创建文件并下载
        if (type === 'xml' || type === 'bpmn') {
          const { err, xml } = await this.bpmnModeler.saveXML()
          console.log('%c [  ]-231', 'font-size:13px; background:pink; color:#bf2c9f;', xml)
          // 读取异常时抛出异常
          if (err) {
            console.error(`[Process Designer Warn ]: ${err.message || err}`)
          }
          let { href, filename } = _this.setEncoded(
            type.toUpperCase(),
            name,
            xml
          )
          downloadFunc(href, filename)
        } else {
          const { err, svg } = await this.bpmnModeler.saveSVG()
          // 读取异常时抛出异常
          if (err) {
            return console.error(err)
          }

          console.log('%c [  ]-304', 'font-size:13px; background:pink; color:#bf2c9f;', svg)
          let { href, filename } = _this.setEncoded('SVG', name, svg)
          downloadFunc(href, filename)
        }
      } catch (e) {
        console.error(`[Process Designer Warn ]: ${e.message || e}`)
      }
      // 文件下载方法
      function downloadFunc(href, filename) {
        if (href && filename) {
          let a = document.createElement('a')
          a.download = filename //指定下载的文件名
          a.href = href //  URL对象
          a.click() // 模拟点击
          URL.revokeObjectURL(a.href) // 释放URL 对象
        }
      }
    },
    downloadProcessAsXml() {
      this.downloadProcess('xml')
    },
    downloadProcessAsBpmn() {
      this.downloadProcess('bpmn')
    },
    downloadProcessAsSvg() {
      this.downloadProcess('svg')
    },
    blobToUrl(blob) {
      return new Promise((resolve, reject) => {
        let reader = new FileReader()
        reader.onload = evt => {
          resolve(evt.target.result)
        }
        reader.onerror = err => {
          reject(err)
        }
        reader.readAsDataURL(blob)
      })
    },
    drawToCanvas(svgUrl) {
      return new Promise((resolve, reject) => {
        const img = new Image()
        // 跨域图片需要添加这个属性，否则画布被污染了无法导出图片
        img.setAttribute('crossOrigin', 'anonymous')
        img.onload = async () => {
          try {
            let canvas = document.createElement('canvas')
            canvas.width = img.width
            canvas.height = img.height
            let ctx = canvas.getContext('2d')
            ctx.fillStyle = '#F6F7F8';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            // 清空画布并填充背景颜色
            ctx.drawImage(img, 0, 0, img.width, img.height)
            resolve(canvas.toDataURL())
          } catch (error) {
            reject(error)
          }
        }
        img.onerror = e => {
          reject(e)
        }
        img.src = svgUrl
      })
    },
    async svgToPng() {
      const { svg } = await this.bpmnModeler.saveSVG()
      let blob = new Blob([svg], {
        type: 'image/svg+xml'
      })
      let svgUrl = await this.blobToUrl(blob)
      let imgData = await this.drawToCanvas(svgUrl)
      let a = document.createElement('a')
      a.href = imgData
      a.download = '图片.png'
      a.click()
    },
    // 加载本地文件
    importLocalFile() {
      const that = this
      const file = this.$refs.refFile.files[0]
      const reader = new FileReader()
      reader.readAsText(file)
      reader.onload = function () {
        let xmlStr = this.result
        that.createNewDiagram(xmlStr)
      }
    },
    processRedo() {
      this.bpmnModeler.get('commandStack').redo()
    },
    processUndo() {
      this.bpmnModeler.get('commandStack').undo()
    },
    /* 创建新的流程图 */
    async createNewDiagram(xml) {
      // 将字符串转换成图显示出来
      const _id = this.$route.query.id || 0;
      console.log('%c [  ]-359', 'font-size:13px; background:pink; color:#bf2c9f;', this.id)
      if (_id) {
        detail({ id: _id }).then(async (res) => {
          const { directoryId, name, status, content } = res.data || {}
          this.directoryId = directoryId;
          this.name = name;
          this.status = status;
          let newId = this.processId || `Process_${new Date().getTime()}`
          let newName = this.processName || `业务流程_${new Date().getTime()}`
          let xmlString = content || DefaultEmptyXML(newId, newName, this.prefix)
          try {
            let { warnings } = await this.bpmnModeler.importXML(xmlString)
            if (warnings && warnings.length) {
              warnings.forEach(warn => console.warn(warn))
            }
            // 创建成功之后的回调，用于事件监听
            this.success()
          } catch (e) {
            console.error(`[Process Designer Warn]: ${e?.message || e}`)
          }
        })

      } else {
        let newId = this.processId || `Process_${new Date().getTime()}`
        let newName = this.processName || `业务流程_${new Date().getTime()}`
        let xmlString = xml || DefaultEmptyXML(newId, newName, this.prefix)
        try {
          let { warnings } = await this.bpmnModeler.importXML(xmlString)
          if (warnings && warnings.length) {
            warnings.forEach(warn => console.warn(warn))
          }
          // 创建成功之后的回调，用于事件监听
          this.success()
        } catch (e) {
          console.error(`[Process Designer Warn]: ${e?.message || e}`)
        }
      }

    },
  },
  // 计算属性
  computed: {
    ...mapState({
      bpmnNodeInfo: state => state.bpmn.nodeInfo,
      bpmnNodeVisible: state => state.bpmn.nodeVisible
    })
  }
}
</script>

<style scoped>
  .containers {
    position: relative;
    background-color: #f6f7f8;
    width: 100%;
    height: calc(100vh - 52px);
  }
  .myStyle {
    position: absolute;
    top: 20px;
    z-index: 111;
    font-weight: bold;
    color: #333333;
    font-size: 18px;
    left: 28px;
  }
  .canvas {
    width: 100%;
    height: 100%;
  }

  .panel {
    position: absolute;
    right: 0;
    top: 0;
    width: 300px;
    /* font-weight: ; */
  }

  .buttons {
    position: absolute;
    left: 20px;
    bottom: 20px;
  }

  .buttons li {
    display: inline-block;
    margin: 5px;
  }

  .buttons li a {
    color: #999;
    background: #eee;
    cursor: not-allowed;
    padding: 8px;
    border: 1px solid #ccc;
    text-decoration: none;
  }

  .buttons li a.active {
    color: #333;
    background: #fff;
    cursor: pointer;
  }

  .modal {
    background-color: rgba(0, 0, 0, 0.6);
    width: 100vw;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
  }

  .modal-content {
    width: 100%;
    height: 100%;
    position: relative;
  }

  .modal-ctx {
    position: absolute;
    width: 300px;
    height: 250px;
    background-color: #fff;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0px 0px 5px 2px rgba(225, 225, 225, 0.8);
  }

  .modal-item {
    padding: 10px;
    width: 100%;
  }

  .right-rail {
    position: absolute;
    top: 5px;
    right: 0;
    height: 99%;
    /* width: 300px; */
    padding: 0 15px;
    z-index: 999;
    background-color: #fff;
  }
  :deep(.el-form-item__label) {
    padding: 0;
  }
  :deep(.el-form-item) {
    margin-bottom: 15px;
  }
  .headerStyle {
    background: #fff;
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 32px;
    border-bottom: 1px solid #e9ecf1;
  }
  .backStyle {
    display: inline-block;
    width: 24px;
    height: 24px;
    background: url('~@/assets/img/back.png') no-repeat center;
    background-size: 24px 24px;
    cursor: pointer;
    margin-right: 28px;
  }
  .publish {
    display: inline-block;
    width: 15px;
    height: 15px;
    background: url('~@/assets/img/publish.png') no-repeat center;
    background-size: cover;
    cursor: pointer;
  }
  .downLoad {
    display: inline-block;
    width: 14px;
    height: 15px;
    background: url('~@/assets/img/down.png') no-repeat center;
    background-size: cover;
    margin-right: 20px;
    cursor: pointer;
  }
  .arrowWrap {
    display: block;
    width: 16px;
    height: 58px;
    background: #ffffff;
    border-radius: 4px 0px 0px 4px;
    border: 1px solid #e9ecf1;
    border-right: 0;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: -15px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
  .arrow {
    display: inline-block;
    width: 9px;
    height: 8px;
    background: url('~@/assets/img/arrow.png') no-repeat center;
  }
  .undo {
    display: inline-block;
    width: 16px;
    height: 16px;
    background: url('~@/assets/img/reback.png') no-repeat center;
    background-size: cover;
    margin-right: 26px;
    position: relative;
    top: -4px;
    cursor: pointer;
  }
  .redo {
    display: inline-block;
    width: 16px;
    height: 16px;
    background: url('~@/assets/img/recover.png') no-repeat center;
    background-size: cover;
    position: relative;
    top: -4px;
    cursor: pointer;
  }
  .nouse {
    cursor: not-allowed;
  }
</style>
